﻿using NLog;
using RestSharp;
using ScreenWarnning.Helpers;
using System.Net;

namespace ScreenWarnning
{
    public class ApiRequest : IDisposable
    {
        private RestClient? _client;
        private RestRequest _request;
        private RestClientOptions _restClientOptions;

        public RestClient Client { get => _client; }

        private RestSharp.Authenticators.IAuthenticator _authenticator;

        public string? Url => _restClientOptions?.BaseUrl?.ToString();

        public void Dispose()
        {
            if (_client != null!)
            {
                _client.Dispose();
                _client = null!;
            }
            _request = null!;
            _restClientOptions = null!;
        }

        public ApiRequest(RestClientOptions restClientOptions, string contentType = "application/json", Method method = Method.Get)
        {
            _restClientOptions = restClientOptions;
            _restClientOptions.CookieContainer = new CookieContainer();
            _request = new RestRequest();
            _request.Timeout = 0;
            _restClientOptions.MaxTimeout = 0;
            _request.AddHeader("Content-Type", contentType);
            _request.Method = method;
        }

        public ApiRequest(string url, string contentType = "application/json", Method method = Method.Get)
            : this(new RestClientOptions(url) { AutomaticDecompression = DecompressionMethods.All }, contentType, method)
        {
        }

        public ApiRequest AddCookies(CookieCollection cookieCollection)
        {
            (_restClientOptions.CookieContainer ?? new CookieContainer()).Add(cookieCollection);
            return this;
        }

        public ApiRequest AddDigestAuth(string username, string password)
        {
            _authenticator = new RestSharp.Authenticators.Digest.DigestAuthenticator(username, password);
            _restClientOptions.Authenticator = _authenticator;
            return this;
        }

        public ApiRequest AddJwtAuth(string token)
        {
            _authenticator = new RestSharp.Authenticators.JwtAuthenticator(token);
            _restClientOptions.Authenticator = _authenticator;
            return this;
        }

        public ApiRequest AddHeaders(Dictionary<string, string> headers)
        {
            if (headers != null!)
                foreach (KeyValuePair<string, string> item in headers)
                {
                    _request.AddHeader(item.Key, item.Value);
                }

            return this;
        }

        public ApiRequest AddBody(object data = null!)
        {
            if (data != null!)
                _request.AddBody(data);
            return this;
        }

        public ApiRequest AddXmlStringBody(string data = null)
        {
            if (data != null!)
                _request.AddStringBody(data, DataFormat.Xml);
            return this;
        }

        public ApiRequest AddJsonBody<T>(T json) where T : class
        {
            if (json != null!)
                _request.AddJsonBody(json);
            return this;
        }

        public ApiRequest AddQueryString(object data = null!)
        {
            if (data == null!) return this;

            string requestData = string.Empty;

            if (data.GetType() == typeof(string))
                requestData = data.ToString()!;
            else
            {
                var props = data.GetType().GetProperties();
                string ret = "";
                foreach (var prop in props)
                {
                    if (string.IsNullOrEmpty(prop.GetValue(data, null)?.ToString())) continue;

                    ret += $"{prop.Name}={prop.GetValue(data, null)?.ToString()}&";
                }
                requestData = ret.TrimEnd('&');
            }
            var paramQuery = requestData!.ToString().Split('&');
            foreach (var item in paramQuery)
            {
                var keyValue = item.Split('=');
                if (keyValue.Length == 2) AddQueryString(keyValue[0], keyValue[1]);
            }
            return this;
        }

        public ApiRequest AddQueryString(string name, string value)
        {
            _request.AddQueryParameter(name, value);
            return this;
        }

        public ApiRequest AddFile(string name, string path, string contentType = "multipart/form-data")
        {
            _request.AddFile(name, path, contentType);
            return this;
        }

        public HttpResult<string> Invoke() => Invoke(new DefaultTConverter());

        public HttpResult<T> Invoke<T>(ITConverter<T> tConverter)
        {
            RestResponse response = null;
            try
            {
                _client = new RestClient(_restClientOptions);
                response = _client.Execute(_request);
            }
            catch (Exception ex)
            {
                return new HttpResult<T>
                {
                    IsSuccess = false,
                    Message = ex.Message,
                    HttpCode = 500,
                    RequestCompleted = false
                };
            }

            return GenerateResultAndReturn<T>(response, tConverter);
        }

        public async Task<HttpResult<string>> InvokeAsync() => await InvokeAsync(new DefaultTConverter());

        public async Task<HttpResult<T>> InvokeAsync<T>(ITConverter<T> tConverter)
        {
            RestResponse response = null;
            try
            {
                _client = new RestClient(_restClientOptions);
                response = await _client.ExecuteAsync(_request);
            }
            catch (Exception ex)
            {
                return new HttpResult<T>
                {
                    IsSuccess = false,
                    Message = ex.Message,
                    HttpCode = 500,
                    RequestCompleted = false
                };
            }

            return GenerateResultAndReturn<T>(response, tConverter);
        }

        public HttpResult<Image> InvokeImage()
        {
            RestResponse response = null;
            try
            {
                _client = new RestClient(_restClientOptions);
                response = _client.Execute(_request);
                if (response.IsSuccessful)
                {
                    using (MemoryStream ms = new MemoryStream(response.RawBytes))
                    {
                        var image = Image.FromStream(ms);
                        return new HttpResult<Image>
                        {
                            IsSuccess = true,
                            HttpCode = (int)response.StatusCode,
                            RequestCompleted = true,
                            Data = image
                        };
                    }
                }

                return new HttpResult<Image>
                {
                    IsSuccess = false,
                    HttpCode = (int)response.StatusCode,
                    RequestCompleted = true,
                };
            }
            catch (Exception ex)
            {
                return new HttpResult<Image>
                {
                    IsSuccess = false,
                    Message = ex.Message,
                    HttpCode = 500,
                    RequestCompleted = false
                };
            }
        }

        private HttpResult<T> GenerateResultAndReturn<T>(RestResponse? restResponse, ITConverter<T> tConverter)
        {
            var HttpResult = new HttpResult<T>();
            try
            {
                if (restResponse!.IsSuccessful)
                {
                    var paramGet = _request.Parameters.GetParameters(ParameterType.QueryString);
                    var paramGetQueryString = paramGet.Any()
                        ? "?" + string.Join("&", paramGet.Select(x => $"{x.Name}={x.Value}").ToList())
                        : "";

                    var cutoffResult = restResponse.Content;

                    if (_request.Method == Method.Get)
                    {
                        NLogHelper.Debug($"接口请求成功 {_restClientOptions.BaseUrl}{paramGetQueryString} {_request.Method}");
                        NLogHelper.Debug($"返回结果: {cutoffResult}");
                    }
                    else
                    {
                        var paramPost = _request.Parameters.GetParameters(ParameterType.RequestBody).ToList();

                        var cutoffBody = paramPost.FirstOrDefault()?.Value?.ToString();

                        NLogHelper.Debug($"接口请求成功 {_restClientOptions.BaseUrl}{paramGetQueryString} {_request.Method} Body: {cutoffBody}");
                        NLogHelper.Debug($"返回结果: {cutoffResult}");
                    }
                }
                else
                {
                    NLogHelper.Error($"接口请求失败 {_restClientOptions.BaseUrl} {_request.Method} - {restResponse.ErrorMessage}");
                }

                if (!restResponse.IsSuccessful)
                {
                    HttpResult = new HttpResult<T>
                    {
                        RequestCompleted = false,
                        IsSuccess = false,
                        HttpCode = (int)restResponse.StatusCode,
                        Message = restResponse.ResponseStatus == ResponseStatus.TimedOut ? "接口请求超时" : (restResponse.StatusDescription + ". " + restResponse.ErrorMessage)
                    };
                }
                else
                    HttpResult = new HttpResult<T>
                    {
                        RequestCompleted = true,
                        IsSuccess = true,
                        HttpCode = (int)restResponse.StatusCode,
                        Data = tConverter == null ? default : tConverter.ToT(restResponse.Content)
                    };

                return HttpResult;
            }
            catch (Exception ex)
            {
                HttpResult.HttpCode = 400;
                HttpResult.IsSuccess = false;
                HttpResult.RequestCompleted = false;
                HttpResult.Message = "发起Http请求失败，" + ex.Message;
                return HttpResult;
            }
        }
    }
}